'''
Created on 07.08.2011

@author: osman
'''

import unittest, opf

Pets = opf.Ontology(uri = '  http://www.example.com/pets   ',
                        en = 'Pets',
                        ru = 'ДомашниеЖивотные')

@Pets.abstract
class Pet:
    def __init__(self, nickname): self.nickname = nickname
    def __repr__(self): return self.__class__.__name__ + " " + self.nickname
    def __str__(self): return self.nickname

@Pets
class Cat(Pet): pass

@Pets
class Dog(Pet): pass

@Pets
class OtherPet(Pet): pass

@Pets
class Snake(Pet):
    @opf.attribute
    def length(self, val): hasLength(self, val)

@Pets.atomic
def hasLength(snake: Snake, val: int) -> 'has length':
    return val > 0

@Pets
class BetterSnake(Snake):
    @opf.attribute
    def length(self, val):
        opf.context().refute(predicate = hasLength, snake = self)
        hasLength(self, val)

Pets.disjoint(Cat, Dog, Snake, OtherPet)

People = opf.Ontology(uri = '  http://www.example.com/people   ',
                        en = 'People',
                        ru = 'Люди')

@People
class Person:
    def __init__(self, name):
        self.name = name
    def __repr__(self): return "Person " + self.name
    def __str__(self): return self.name

@People.atomic
def isCloseRelativeOf(human1: Person, human2: Person): return True

@Pets.atomic
def hasOwner(pet: Pet, owner: Person) -> 'has owner':
    return True

@Pets
def hasCoOwner(pet: Pet, owner: opf.exists(Person), coOwner: Person) -> 'has co-owner':
    hasOwner(pet, owner), isCloseRelativeOf(owner, coOwner)

class OntologyTestCase(unittest.TestCase):
    def test_instances(self):
        Pets.eraseGraph()
        with Pets:
            Tiger = Cat("Tiger")
            Max = Dog("Max")
            Bob = OtherPet("Bob")
        self.assertIn(Tiger, Pets.instances)
        self.assertIn(Max, Pets.instances)
        self.assertIn(Bob, Pets.instances)
    def test_instantiatingAbstractClass(self):
        with self.assertRaises(opf.InstantiatingAbstractClass):
            with Pets: Pet("Bob")
    def test_disjointError(self):
        with self.assertRaises(opf.DisjointError):
            @Pets
            class ImpossiblePet(Cat, Dog): pass
    def test_atomicPredicateObjectType(self):
        Pets.eraseGraph()
        with Pets:
            Tiger = Cat("Tiger")
            Oscar = Cat("Oscar")
            Robert = Person("Robert")
            Max = Dog("Max")
            hasOwner(pet = Tiger, owner = Robert)
            hasOwner(Oscar, Robert)
        self.assertIn((0, Tiger, hasOwner, Robert), Pets.graph)
        self.assertIn((0, Oscar, hasOwner, Robert), Pets.graph)
        with self.assertRaises(opf.AtomicPredicateObjectTypeError):
            hasOwner(Tiger, Max)
    def test_atomicPredicate(self):
        msg = "atomic predicate error"
        Pets.eraseGraph()
        with Pets:
            lola = Snake('Lola')
            hasLength(lola, 20)
        self.assertIn((0, lola, hasLength, 20), Pets.graph, msg)
    def test_attribute(self):
        msg = "semantic attribute error"
        Pets.eraseGraph()
        with Pets:
            lola = Snake("Lola")
            lola.length = 20
        self.assertEqual(lola.length, 20, msg)
        self.assertIn((0, lola, hasLength, 20), Pets.graph, msg)
    def test_atomicPredicateCondition(self):
        Pets.eraseGraph()
        with Pets:
            lola = Snake("Lola")
        wasCount = Pets.count(predicate = hasLength, snake = lola)
        hasLength(lola, -1)
        self.assertEqual(Pets.count(predicate = hasLength, snake = lola),
                         wasCount)
    def test_atomicPredicateFind(self):
        msg = "ontology 'find' method error on atomic predicate"
        Pets.eraseGraph()
        with Pets:
            Tiger = Cat("Tiger")
            Oscar = Cat("Oscar")
            Max = Dog("Max")
            Robert = Person("Robert")
            John = Person("John")
            hasOwner(Max, John)
            hasOwner(Tiger, Robert)
            hasOwner(Oscar, Robert)
        def _decode(t, s, p, o):
            return opf.freezeResult(hasOwner.decodeQuadruple(t,s,p,o))
        self.assertIn(_decode(0, Tiger, hasOwner, Robert),
                      opf.freezeResults(Pets.find(predicate = hasOwner)), msg)
        self.assertIn(_decode(0, Oscar, hasOwner, Robert),
                      opf.freezeResults(Pets.find(predicate = hasOwner)), msg)
        self.assertIn(_decode(0, Max, hasOwner, John),
                      opf.freezeResults(Pets.find(predicate = hasOwner,
                                                  owner = John)), msg)
        self.assertIn(_decode(0, Oscar, hasOwner, Robert),
                      opf.freezeResults(Pets.find(predicate = hasOwner,
                                                  owner = Robert)), msg)
        self.assertIn(_decode(0, Tiger, hasOwner, Robert),
                      opf.freezeResults(Pets.find(pet = Tiger,
                                                  predicate = hasOwner,
                                                  owner = Robert)), msg)
    def test_atomicPredicateMatch(self):
        msg = "ontology 'match' method error on atomic predicate"
        Pets.eraseGraph()
        with Pets:
            Tiger = Cat("Tiger")
            Oscar = Cat("Oscar")
            Max = Dog("Max")
            Robert = Person("Robert")
            John = Person("John")
            hasOwner(Max, John)
            hasOwner(Tiger, Robert)
            hasOwner(Oscar, Robert)
        self.assertTrue(Pets.match(pet = Max, predicate = hasOwner, owner = John), msg)
        self.assertTrue(Pets.match(pet = Tiger, predicate = hasOwner, owner = Robert), msg)
        self.assertTrue(Pets.match(pet = Oscar, predicate = hasOwner, owner = Robert), msg)
    def test_atomicPredicateCount(self):
        msg = "ontology 'count' method error on atomic predicate"
        Pets.eraseGraph()
        with Pets:
            Tiger = Cat("Tiger")
            Oscar = Cat("Oscar")
            Max = Dog("Max")
            Robert = Person("Robert")
            John = Person("John")
            hasOwner(Max, John)
            hasOwner(Tiger, Robert)
            hasOwner(Oscar, Robert)
        self.assertEqual(Pets.count(predicate = hasOwner, owner = Robert), 2, msg)
        self.assertEqual(Pets.count(predicate = hasOwner, owner = John), 1, msg)
    def test_atomicPredicateRefute(self):
        msg = "ontology 'refute' method error on atomic predicate"
        Pets.eraseGraph()
        with Pets:
            # BetterSnake uses 'refute' method in 'length' attribute definition
            lola = BetterSnake('Lola')
            lola.length = 20
            lola.length = 30
        self.assertEqual(Pets.count(snake = lola, predicate = hasLength), 1, msg)
    def test_predicateAdd1(self):
        msg = 'adding compound predicate error'
        family = opf.Ontology()
        with family:
            Tiger = Cat("Tiger")
            Robert = Person("Robert")
            Mary = Person("Mary")
            hasOwner(Tiger, Robert)
            hasCoOwner(pet = Tiger, owner = Robert, coOwner = Mary)
        self.assertIn((0, Robert, isCloseRelativeOf, Mary),
                      family.graph, msg)
    def test_predicateAdd2(self):
        msg = 'adding compound predicate error'
        family = opf.Ontology()
        with family:
            Tiger = Cat("Tiger")
            Robert = Person("Robert")
            Mary = Person("Mary")
            hasOwner(Tiger, Robert)
            hasCoOwner(Tiger, Robert,  Mary)
        self.assertIn((0, Robert, isCloseRelativeOf, Mary),
                      family.graph, msg)
    def test_predicateCount(self):
        msg = 'count compound predicate error'
        family = opf.Ontology()
        with family:
            Tiger = Cat("Tiger")
            Oscar = Cat("Oscar")
            Robert = Person("Robert")
            Mary = Person("Mary")
            hasOwner(Tiger, Robert)
            isCloseRelativeOf(Robert, Mary)
        self.assertEqual(family.count(predicate = hasCoOwner), 1, msg)
        self.assertEqual(family.count(predicate = hasCoOwner, coOwner = Mary),
                                  1, msg)
        self.assertEqual(family.count(pet = Oscar, predicate = hasCoOwner, coOwner = Mary),
                                  0, msg)
    def test_predicateFind(self):
        msg = 'find compound predicate error'
        family = opf.Ontology()
        with family:
            Tiger = Cat("Tiger")
            Oscar = Cat("Oscar")
            Robert = Person("Robert")
            Mary = Person("Mary")
            hasOwner(Tiger, Robert)
            isCloseRelativeOf(Robert, Mary)
        result = opf.freezeResults(family.find(predicate = hasCoOwner))
        self.assertIn(frozenset((('pet', Tiger), ('coOwner', Mary), 
                                ('predicate', hasCoOwner))), result, msg)
        self.assertNotIn(frozenset((('pet', Oscar), ('coOwner', Mary), 
                                ('predicate', hasCoOwner))), result, msg)
        result = opf.freezeResults(family.find(predicate = hasCoOwner,
                                               coOwner = Mary))
        self.assertIn(frozenset((('pet', Tiger), ('coOwner', Mary),
                                ('predicate', hasCoOwner))), result, msg)
        self.assertNotIn(frozenset((('pet', Oscar), ('coOwner', Mary),
                                ('predicate', hasCoOwner))), result, msg)
        result = opf.freezeResults(family.find(pet = Tiger,
                                               predicate = hasCoOwner,
                                               coOwner = Mary))
        self.assertIn(frozenset((('pet', Tiger), ('coOwner', Mary),
                                ('predicate', hasCoOwner))), result, msg)
        self.assertNotIn(frozenset((('pet', Oscar), ('coOwner', Mary),
                                ('predicate', hasCoOwner))), result, msg)
    def test_predicateMatch(self):
        msg = 'match compound predicate error'
        family = opf.Ontology()
        with family:
            Tiger = Cat("Tiger")
            Oscar = Cat("Oscar")
            Robert = Person("Robert")
            Mary = Person("Mary")
            hasOwner(Tiger, Robert)
            isCloseRelativeOf(Robert, Mary)
        self.assertTrue(family.match(pet = Tiger, predicate = hasCoOwner, coOwner = Mary), msg)
        self.assertFalse(family.match(pet = Oscar, predicate = hasCoOwner, coOwner = Mary), msg)
    def noTest_predicateRefute(self):
        # TODO: this test does not work since Container.delete doesn't
        msg = 'refuting compound predicate error'
        family = opf.Ontology()
        with family:
            Tiger = Cat("Tiger")
            Robert = Person("Robert")
            Mary = Person("Mary")
            hasOwner(Tiger, Robert)
            isCloseRelativeOf(Robert, Mary)
        family.refute(predicate = hasCoOwner)
        self.assertNotIn((0, Robert, isCloseRelativeOf, Mary),
                      family.graph, msg)
        self.assertNotIn((0, Tiger, hasOwner, Robert),
                      family.graph, msg)
    def test_unaryPredicate(self):
        @Pets.atomic
        def isDog(pet: Pet):
            return isinstance(pet, Dog)
        Pets.eraseGraph()
        with Pets:
            Oscar = Cat("Oscar")
            Max = Dog("Max")
            isDog(Oscar)
            isDog(Max)
        self.assertEqual(Pets.count(predicate = isDog), 1)
    def test_nullaryPredicate(self):
        @Pets.atomic
        def isOntology(): return True
        Pets.eraseGraph()
        with Pets: isOntology()
        self.assertEqual(Pets.count(predicate = isOntology), 1)
    def test_realizer(self):
        family = opf.Ontology()
        with family:
            Tiger = Cat("Tiger")
            Robert = Person("Robert")
            Mary = Person("Mary")
            hasOwner(Tiger, Robert)
            isCloseRelativeOf(Robert, Mary)
        result = None
        @opf.realizer(hasCoOwner, coOwner = Mary)
        def onCoOwner(pet, predicate, coOwner):
            nonlocal result
            result = "%s %s %s" % (str(pet), str(hasCoOwner), str(coOwner))
        family.realize(onCoOwner)
        self.assertEqual(result, "Tiger has co-owner Mary")
    def test_inClassRealizer(self):
        family = opf.Ontology()
        with family:
            Tiger = Cat("Tiger")
            Robert = Person("Robert")
            Mary = Person("Mary")
            hasOwner(Tiger, Robert)
            isCloseRelativeOf(Robert, Mary)
        result = None
        class MyRealizers:
            @opf.realizer(hasCoOwner, coOwner = Mary)
            def onCoOwner(self, pet, predicate, coOwner):
                nonlocal result
                result = "%s %s %s" % (str(pet), str(hasCoOwner), str(coOwner))
        r = MyRealizers()
        family.realize(r)
        self.assertEqual(result, "Tiger has co-owner Mary")
    def test_twiceRealizer(self):
        family = opf.Ontology()
        with family:
            Tiger = Cat("Tiger")
            Robert = Person("Robert")
            Mary = Person("Mary")
            hasOwner(Tiger, Robert)
            isCloseRelativeOf(Robert, Mary)
        result = []
        count = 0
        @opf.realizer(hasCoOwner)
        def onCoOwner(pet, predicate, coOwner):
            nonlocal result, count, family
            if count == 0:
                with family:
                    Oscar = Cat("Oscar")
                    isCloseRelativeOf(Mary, Robert)
                    hasOwner(Oscar, Mary)
            else:
                result.append("%s %s %s" % (str(pet),
                                            str(hasCoOwner),
                                            str(coOwner)))
            count += 1
        family.realize(onCoOwner)
        self.assertEqual(count, 3)
        self.assertIn("Tiger has co-owner Mary", set(result))
        self.assertIn("Oscar has co-owner Robert", set(result))
    def test_topics(self):
        family = opf.Ontology()
        t1 = family.topic()
        @family.atomic
        def know(person: Person, what: opf.Topic):
            return True
        with t1:
            Tiger = Cat("Tiger")
            Robert = Person("Robert")
            Mary = Person("Mary")
            hasOwner(Tiger, Robert)
            isCloseRelativeOf(Robert, Mary)
        with family:
            Ivan = Person("Ivan")
            know(Ivan, t1)
        self.assertIn((t1, Tiger, hasOwner, Robert), family.graph)
        self.assertIn((t1, Robert, isCloseRelativeOf, Mary), family.graph)
        self.assertIn((0, Ivan, know, t1), family.graph)
        
